package com.absir.android.app;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import android.app.Application;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;

import com.absir.android.context.ContextUtils;
import com.absir.android.view.InjectViewUtils;
import com.absir.bean.basis.BeanConfig;
import com.absir.bean.basis.Configure;
import com.absir.bean.core.BeanConfigImpl;
import com.absir.bean.core.BeanFactoryProvider;
import com.absir.bean.core.BeanFactoryUtils;
import com.absir.bean.core.BeanScanner;
import com.absir.bean.inject.InjectInvoker;
import com.absir.core.kernel.KernelArray;
import com.absir.core.kernel.KernelLang;
import com.absir.core.kernel.KernelLang.CallbackBreak;
import com.absir.core.kernel.KernelObject;
import com.absir.core.kernel.KernelReflect;
import com.absir.core.util.UtilPackage;

/**
 * Copyright 2013 ABSir's Studio
 * 
 * All right reserved
 *
 * Create on 2013-6-9 下午3:58:36
 */

/**
 * @author absir
 * 
 *         <exclusions> <exclusion> <groupId>com.google.android</groupId>
 *         <artifactId>android</artifactId> </exclusion> </exclusions>
 */
@Configure
public class ApplicationBean extends Application {

	/** Application */
	private static Application Application;

	/**
	 * @return
	 */
	public static Application getApplication() {
		return Application;
	}

	/**
	 * @return
	 */
	protected boolean isAutoInstrumentation() {
		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Application#onCreate()
	 */
	@Override
	public final void onCreate() {
		Application = this;
		InjectViewUtils.setApplication(this);
		if (isAutoInstrumentation()) {
			Object mainThread = KernelObject.declaredGet(getBaseContext(), "mMainThread");
			Field instrumentationField = KernelReflect.declaredField(mainThread.getClass(), "mInstrumentation");
			Object instrumentation = new InstrumentationBean();
			KernelObject.copy(KernelReflect.get(mainThread, instrumentationField), instrumentation);
			KernelReflect.set(mainThread, instrumentationField, instrumentation);
		}

		BeanFactoryProvider provider = getBeanFactoryProvider();
		InjectInvoker[][] injectInvokers = ContextUtils.getCreateInvokers(this.getClass(), true);
		for (InjectInvoker injectInvoker : injectInvokers[0]) {
			injectInvoker.invoke(BeanFactoryUtils.get(), this);
		}

		super.onCreate();
		provider.started();
		provider.getBeanFactoryStoppings();
		for (InjectInvoker injectInvoker : injectInvokers[1]) {
			injectInvoker.invoke(BeanFactoryUtils.get(), this);
		}
	}

	/**
	 * @author absir
	 * 
	 */
	protected class DalvikScanner extends BeanScanner {

		/*
		 * (non-Javadoc)
		 * 
		 * @see
		 * com.absir.bean.core.BeanScanner#scanBeanTypes(java.lang.String[],
		 * com.absir.core.kernel.KernelLang.CallbackBreak)
		 */
		@Override
		protected void scanBeanTypes(String[] packageNames, CallbackBreak<String> callbackBreak) {
			for (String packageName : packageNames) {
				UtilPackage.findClasses(packageName, true, callbackBreak);
				DalvikHelper.doScannerFilter(ApplicationBean.this, callbackBreak, null);
			}
		}
	}

	/**
	 * @author absir
	 * 
	 */
	protected static class DalvikFactoryProvider extends BeanFactoryProvider {

		/**
		 * @param includePackages
		 * @param excludePackages
		 * @param unMatchPatterns
		 */
		public DalvikFactoryProvider(Collection<String> includePackages, Collection<String> excludePackages, Collection<String> unMatchPatterns) {
			super(includePackages, excludePackages, unMatchPatterns);
			// TODO Auto-generated constructor stub
		}

		/**
		 * @return
		 */
		protected BeanConfig getBeanConfig() {
			return new BeanConfigImpl(DalvikFactoryProvider.this, "file:///android_asset/");
		}
	}

	/**
	 * @param metaData
	 * @param key
	 * @return
	 */
	private String[] getStringArray(Bundle metaData, String key) {
		if (metaData == null) {
			return null;
		}

		String value = metaData.getString(key);
		if (value == null) {
			return KernelLang.NULL_STRINGS;
		}

		return value.split(",");
	}

	/**
	 * @return
	 */
	private BeanFactoryProvider getBeanFactoryProvider() {
		Bundle metaData = null;
		try {
			metaData = this.getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA).metaData;

		} catch (NameNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		List<String> includePackages = metaData == null ? null : KernelArray.toList(getStringArray(metaData, "includePackages"));
		if (includePackages == null) {
			includePackages = new ArrayList<String>();
			includePackages.add(getPackageName());

		} else if (includePackages.size() <= 0) {
			includePackages.add(getPackageName());
		}

		BeanFactoryProvider beanFactoryProvider = new DalvikFactoryProvider(includePackages, metaData == null ? null : KernelArray.toList(getStringArray(metaData, "excludePackages")),
				metaData == null ? null : KernelArray.toList(getStringArray(metaData, "unMatchPatterns")));
		beanFactoryProvider.scan(new DalvikScanner(), null, this);
		return beanFactoryProvider;
	}
}
